package villapancha2011;

import java.util.List;
import java.util.LinkedList;
import java.util.Random;

import org.javahispano.javacup.modelo.*;

// Zona de Ennumerados -------------------
enum Grosso {
    Rava, Manolo, Diegoa, Cyber, Chino, Pasto, Costilla, Pela, Josexo, Bochae, Montico, Ninguno
};

enum Sector {
    Ataque, Medio, Defensa
};

enum Posesion {
    Atancando, // villa pancha tiene la posesion
    Defendiendo, // el rival tiene el balon
    EnDisputa
};

enum TipoRemate {
    Pasar, Chutar, Regatear
};
// Fin ennumerados  --------------------------


public class TacticaVillaPancha2011 implements Tactica {

    Posicion alineacion1[] = new Posicion[]{
        //new Posicion(0.2595419847328244, -50.41044776119403),
        new Posicion(0.2595419847328244, -Constantes.LARGO_CAMPO_JUEGO / 2),
        new Posicion(9.034965034965035, -33.49547511312217),
        new Posicion(-8.797202797202797, -29.694570135746606),
        new Posicion(27.104895104895103, -26.84389140271493),
        new Posicion(0.23776223776223776, -7.364253393665159),
        new Posicion(-25.44055944055944, -23.28054298642534),
        new Posicion(16.405594405594407, 33.49547511312217),
        new Posicion(22.58741258741259, 4.513574660633484),
        new Posicion(0.0, 41.334841628959275),
        new Posicion(-24.48951048951049, 14.25339366515837),
        new Posicion(0.951048951048951, 19.2420814479638)
    };
    Posicion alineacion2[] = new Posicion[]{
        new Posicion(0.2595419847328244, -50.41044776119403),
        new Posicion(-11.16030534351145, -31.082089552238806),
        new Posicion(11.16030534351145, -31.6044776119403),
        new Posicion(27.251908396946565, -27.94776119402985),
        new Posicion(-29.84732824427481, -26.902985074626866),
        new Posicion(8.564885496183205, -7.574626865671642),
        new Posicion(-10.641221374045802, -7.052238805970149),
        new Posicion(27.251908396946565, 4.440298507462686),
        new Posicion(-29.32824427480916, 3.3955223880597014),
        new Posicion(-0.2595419847328244, 19.067164179104477),
        new Posicion(-0.2595419847328244, 35.78358208955224)
    };
    Posicion alineacion3[] = new Posicion[]{
        new Posicion(0.2595419847328244, -50.41044776119403),
        new Posicion(-11.16030534351145, -31.082089552238806),
        new Posicion(11.16030534351145, -31.6044776119403),
        new Posicion(26.732824427480914, -20.111940298507463),
        new Posicion(-29.32824427480916, -21.67910447761194),
        new Posicion(0.2595419847328244, -0.26119402985074625),
        new Posicion(-18.946564885496183, -0.26119402985074625),
        new Posicion(18.946564885496183, -0.26119402985074625),
        new Posicion(-19.46564885496183, 35.78358208955224),
        new Posicion(-0.2595419847328244, 19.067164179104477),
        new Posicion(18.946564885496183, 35.26119402985075)
    };
    Posicion alineacion4[] = new Posicion[]{
        new Posicion(0.2595419847328244, -50.41044776119403),
        new Posicion(-11.16030534351145, -31.082089552238806),
        new Posicion(11.16030534351145, -31.6044776119403),
        new Posicion(28.290076335877863, -28.470149253731343),
        new Posicion(-28.290076335877863, -28.470149253731343),
        new Posicion(11.16030534351145, -1.3059701492537314),
        new Posicion(-10.641221374045802, -0.7835820895522387),
        new Posicion(-27.251908396946565, 31.6044776119403),
        new Posicion(-10.641221374045802, 30.559701492537314),
        new Posicion(9.603053435114505, 28.992537313432837),
        new Posicion(25.69465648854962, 28.992537313432837)
    };
    Posicion alineacion5[] = new Posicion[]{
        new Posicion(0.2595419847328244, -50.41044776119403),
        new Posicion(11.174825174825173, -34.920814479638004),
        new Posicion(-6.181818181818182, -30.407239819004527),
        new Posicion(28.293706293706293, -31.59502262443439),
        new Posicion(1.902097902097902, -18.054298642533936),
        new Posicion(-28.531468531468533, -26.84389140271493),
        new Posicion(22.11188811188811, -2.8506787330316743),
        new Posicion(15.216783216783217, -9.97737556561086),
        new Posicion(-1.4265734265734267, -0.9502262443438915),
        new Posicion(-22.58741258741259, -2.3755656108597285),
        new Posicion(1.902097902097902, -1.1877828054298643)
    };
    Posicion alineacion6[] = new Posicion[]{
        new Posicion(0.2595419847328244, -50.41044776119403),
        new Posicion(9.986013986013985, -38.4841628959276),
        new Posicion(-8.321678321678322, -35.8710407239819),
        new Posicion(28.290076335877863, -28.470149253731343),
        new Posicion(-2.8531468531468533, -19.004524886877828),
        new Posicion(-27.34265734265734, -28.98190045248869),
        new Posicion(24.965034965034967, -5.9389140271493215),
        new Posicion(4.5174825174825175, -14.96606334841629),
        new Posicion(11.174825174825173, -2.3755656108597285),
        new Posicion(-25.678321678321677, -6.651583710407239),
        new Posicion(-10.937062937062937, -4.038461538461538)
    };

    @Override
    public TacticaDetalle getDetalle() {
        return detalle;
    }

    @Override
    public Posicion[] getPosicionSaca(SituacionPartido sp) {
        return alineacion5;
    }

    @Override
    public Posicion[] getPosicionRecive(SituacionPartido sp) {
        return alineacion6;
    }

    
   
    // Zona de constantes --------------------------
    static double LARGO_SECTOR_DEFENSA = Constantes.LARGO_CAMPO_JUEGO * 0.35;
    static double LARGO_SECTOR_MEDIO = Constantes.LARGO_CAMPO_JUEGO * 0.65;
    static double LARGO_SECTOR_ATAQUE = Constantes.LARGO_CAMPO_JUEGO * 1;
    // Fin constantes
    // Zona de atributos ---------------------------
    Sector sectorActual = Sector.Medio;
    Posesion posesionActual = Posesion.EnDisputa;
    Random rand = new Random();
    LinkedList<Comando> comandos = new LinkedList<Comando>();
    SituacionPartido situacionPartido;
    LinkedList<Grosso> jugadoresDisponibles = new LinkedList<Grosso>();
    Posicion alineacionActual[] = alineacion1;
    Grosso grossoUltimoPase = Grosso.Ninguno;
    Grosso receptorUltimoPase = Grosso.Ninguno;

    TacticaDetalle detalle = new TacticaDetalleVillaPancha2011();
    Arquera arquera = new Arquera(this);
    Pase pase = new Pase(this);
    Chutar chutar = new Chutar(this);
    Ubicarse ubicarse = new Ubicarse(this);
    Ataque ataque = new Ataque(this);

    Estrategia estrategia = new Estrategia(this);
    
    // End atributos ------------------------------------
    // Zona metodos auxiliares
    Grosso getGrosso(int i) {
        for (Grosso g : Grosso.values()) {
            if (g.ordinal() == i) {
                return g;
            }
        }
        return Grosso.Ninguno;
    }

    Sector getSector() {
        double yPelota = situacionPartido.balon().moverPosicion(0, Constantes.LARGO_CAMPO_JUEGO / 2).getY();
        if (yPelota <= LARGO_SECTOR_DEFENSA) {
            return Sector.Defensa;
        }
        if (yPelota <= LARGO_SECTOR_MEDIO) {
            return Sector.Medio;
        } else {
            return Sector.Ataque;
        }
    }

    Sector getSectorPosicion(Posicion p) {
        p = p.moverPosicion(0, Constantes.LARGO_CAMPO_JUEGO / 2);
        if (p.getY() <= LARGO_SECTOR_DEFENSA) {
            return Sector.Defensa;
        }
        if (p.getY() <= LARGO_SECTOR_MEDIO) {
            return Sector.Medio;
        } else {
            return Sector.Ataque;
        }
    }

    Sector getSectorGrosso(Grosso g) {
        Posicion p = situacionPartido.misJugadores()[g.ordinal()];
        p = p.moverPosicion(0, Constantes.LARGO_CAMPO_JUEGO / 2);
        if (p.getY() <= LARGO_SECTOR_DEFENSA) {
            return Sector.Defensa;
        }
        if (p.getY() <= LARGO_SECTOR_MEDIO) {
            return Sector.Medio;
        } else {
            return Sector.Ataque;
        }
    }

    boolean esGrossoDisponible(Grosso g) {
        return jugadoresDisponibles.contains(g);
    }

    void MarcarGrossoUtilizado(Grosso g) {
        jugadoresDisponibles.remove(g);
    }

    /* Jorgeao: Calcula a que distancia está el rival mas cercano, se usa para
    determinar si puedo seguir teniendo la pelota o si tengo que pasarla o tirar */
    double distanciaRivalMasCercanoAPelota() {
        double distancia = 1000.0;
        for (Posicion pos : situacionPartido.rivales()) {
            double d = situacionPartido.balon().distancia(pos);
            if (distancia > d) {
                distancia = d;
            }
        }

        return distancia;
    }

    /**
     *
     * @param posicion Posición del punto a partir del cual buscar
     * @param distancia Distancia máxima de los rivales
     * @return Busca los rivales que están a menos de distancia de posicion
     */
    int[] rivalesCercanos(Posicion posicion, double distancia) {
        LinkedList<Integer> cercanos = new LinkedList<Integer>();
        for (int i = 0; i < situacionPartido.rivales().length; i++) {
            Posicion p = situacionPartido.rivales()[i];
            if (posicion.distancia(p) <= distancia) {
                cercanos.add(i);
            }
        }
        int[] result = new int[cercanos.size()];
        for (int i = 0; i < cercanos.size(); i++) {
            result[i] = cercanos.get(i).intValue();
        }

        return result;
    }

    int iteracionesMarcadorCercano(Grosso g) {
        Posicion posicion = situacionPartido.misJugadores()[g.ordinal()];
        int iteraciones = 1000;
        for (int i = 0; i < situacionPartido.rivales().length; i++) {
            Posicion p = situacionPartido.rivales()[i];
            int iteracionesLocal = (int) (posicion.distancia(p) /
                    Constantes.getVelocidad(situacionPartido.getVelocidadJugadorRival(i)));
            if (iteracionesLocal < iteraciones) {
                iteraciones = iteracionesLocal;
            }
        }
        return iteraciones;
    }

    int iteracionesMarcadorCercano(Posicion posicion) {
        int iteraciones = 1000;
        for (int i = 0; i < situacionPartido.rivales().length; i++) {
            Posicion p = situacionPartido.rivales()[i];
            int iteracionesLocal = (int) (posicion.distancia(p) /
                    Constantes.getVelocidad(situacionPartido.getVelocidadJugadorRival(i)));
            if (iteracionesLocal < iteraciones) {
                iteraciones = iteracionesLocal;
            }
        }
        return iteraciones;
    }

    /**
     *
     * @param posicion Posición del punto a partir del cual buscar
     * @param distanciaMin Distancia mínima de los rivales
     * @param distanciaMax Distancia máxima de los rivales
     * @return Busca los rivales que están a mas de distanciaMin y menos de distanciaMax metros de posicion
     */
    int[] rivalesCercanos(Posicion posicion, double distanciaMin, double distanciaMax) {
        LinkedList<Integer> cercanos = new LinkedList<Integer>();
        for (int i = 0; i < situacionPartido.rivales().length; i++) {
            Posicion p = situacionPartido.rivales()[i];
            if (posicion.distancia(p) <= distanciaMax && posicion.distancia(p) >= distanciaMin) {
                cercanos.add(i);
            }
        }
        int[] result = new int[cercanos.size()];
        for (int i = 0; i < cercanos.size(); i++) {
            result[i] = cercanos.get(i).intValue();
        }

        return result;
    }

    boolean grossoPuedeRematar(Grosso grosso) {
        for (int indiceGrosso : situacionPartido.puedenRematar()) {
            if (grosso.ordinal() == indiceGrosso) {
                return true;
            }
        }
        return false;
    }

    boolean grossosPuedenRematar() {
        return situacionPartido.puedenRematar().length > 0;
    }

    boolean rivalesPuedenRematar() {
        return situacionPartido.puedenRematarRival().length > 0;
    }

    

    // End metodos auxiliares
    // Logica de negocios de paco casal
    void inicializar() {
        comandos.clear();

        // Inicializo la lista de jugadores a los que puedo dar ordenes
        // Excluye a la rava
        jugadoresDisponibles.clear();
        for (int i = 1; i < 11; i++) {
            jugadoresDisponibles.add(getGrosso(i));
        }

        sectorActual = getSector();

        alineacionActual = estrategia.getPosicionesParaUbicarse();

    }

    void evaluarTacticaActual() {
        //TODO: Implementar
    }

    
    

    // ***********************
    TipoRemate decidirRemate() {
        // TODO: Esto es un primer intento

        double distanciaRivalCercano = distanciaRivalMasCercanoAPelota();
        double yPelota = situacionPartido.balon().moverPosicion(0, Constantes.LARGO_CAMPO_JUEGO / 2).getY();

        if (sectorActual == Sector.Ataque) {
            //if (yPelota > Constantes.LARGO_CAMPO_JUEGO * 0.75) {
            if (situacionPartido.balon().distancia(Constantes.centroArcoSup) < 30) {
                double anguloArco = situacionPartido.balon().angulo(Constantes.centroArcoSup);
                anguloArco = ((anguloArco * 90d) * Math.PI) / 180d;
                double anguloValido = 110d;
                // No patear si el angulo de tiro con respecto al arco esta entre 55 y 90
                if (anguloArco < anguloValido / 2 ) {
                    return TipoRemate.Chutar;
                }
                if (anguloArco > 2 * Math.PI - anguloValido / 2 ) {
                    return TipoRemate.Chutar;
                }

            } else if (iteracionesMarcadorCercano(situacionPartido.balon()) > Constantes.FPS * 3) {
                return TipoRemate.Regatear;
            }
        } else if (sectorActual == Sector.Medio) {
            // Debe demorar dos segundos en llegar un rival
            if (iteracionesMarcadorCercano(situacionPartido.balon()) > Constantes.FPS * 4) {
                return TipoRemate.Regatear;
            }
        }

        return TipoRemate.Pasar;

    }

     void regatear() {
        //TODO: Implementar

        // Jorgeao - Temp implementacion para que no se quede quieto 
        // Avanza con el balon
        for (int i : situacionPartido.puedenRematar()) {
            if (esGrossoDisponible(getGrosso(i))) {
                comandos.add(new ComandoIrA(i, situacionPartido.balon()));
                comandos.add(new ComandoGolpearBalon(i, Constantes.centroArcoSup, 0.4, 0));
                MarcarGrossoUtilizado(getGrosso(i));
            }
        }

    }

    void accionesRivalesRematando() {
        //TODO: Implementar
        // Baja Prioridad
    }

    void accionesBalonEnDisputa() {
        //TODO: Implementar
        // Baja Prioridad
    }

    void defender() {
        //TODO: Implementar
    }

    void atacar() {
        //TODO: Implementar
    }

    
    // End logica de negocios
    @Override
    public List<Comando> ejecutar(SituacionPartido sp) {

        situacionPartido = sp;
        
        inicializar();

        if (!sp.saco()) {
            MarcarGrossoUtilizado(grossoUltimoPase);
        } else {
            grossoUltimoPase = Grosso.Ninguno;
        }
        evaluarTacticaActual();

        arquera.arquera();

        // Acciones para los jugadores que pueden patear o pasar

        boolean grossosRematan = grossosPuedenRematar();
        boolean rivalesRematan = rivalesPuedenRematar();

        if (grossosRematan && rivalesRematan) {
            accionesBalonEnDisputa();
        }

        if (grossosRematan) {
            TipoRemate caso = decidirRemate();

            switch (caso) {
                case Pasar:
                    pase.pasar();
                    break;
                case Chutar:
                    chutar.chutar();
                    break;
                case Regatear:
                    regatear();
                    break;
            }
        }

        if (rivalesRematan) {
            accionesRivalesRematando();
        }


        // Acciones para el resto de los jugadores, los alejados de la pelota

        defender();
        ataque.atacar();
        ubicarse.ubicarse();

        //retorna la lista de comandos.
        return comandos;
    }
}
